
#include <stdio.h>
#include <cassert>
#include <string.h>
#include <inttypes.h>
#include <stack>


using namespace std;
#include "cbp3_def.h"
#include "cbp3_framework.h"

#define MAX_ADDRESSES 32
#define PRED_SIZE 14

uint32_t* path_table_fetch;
uint32_t* path_table_ret;

int empty_slot_f;
int empty_slot_r;

int8_t prediction_table;

uint32_t RotateRight32Bit(uint32_t data, unsigned int bits) {
  return ((data >> bits) | (data << (32-bits));
}
uint32_t RotateLeft32Bit(uint32_t data, unsigned int bits) {
  return ((data << bits) | (data >> (32-bits));
}


uint32_t computeHash_fetch(int n) {
    uint32_t res = path_table_fetch[0];
    for (int i=1 ; i < n ; i++)
      res ^= RotateRight32Bit(path_table_fetch[i] , i);

    return res;
}

uint32_t computeHash_ret(int n) {
    uint32_t res = path_table_ret[0];
    for (int i=1 ; i < n ; i++)
      res ^= RotateRight32Bit(path_table_ret[i] , i);

    return res;
}



void PredictorInit() {
	path_table_fetch = new uint32_t[MAX_ADDRESSES];
	path_table_ret = new uint32_t[MAX_ADDRESSES];
	
	weights = new int[MAX_ADDRESSES];
	empty_slot_f = empty_slot_r = 0;
	prediction_table = new int8_t[1 << PRED_SIZE];
}

void PredictorReset() {
        for (int i = 0; i < (1 << PRED_SIZE); i ++)
	  prediction_table[i] = 0;
	for (int i = 0; i < MAX_ADDRESSES; i++)
	  path_table_fetch[i] = path_table_ret[i] = 0;
       

}

void add_addr_fetch(uint32_t addr) {
  if (empty_slot_f == MAX_ADDRESSES) {
    empty_slot_f = 1;
    path_table_fetch[0] = addr;
  } else {
    path_table_fetch[empty_slot_f++] = addr; 
  }
}

void add_addr_ret(uint32_t addr) {
  if (empty_slot_r == MAX_ADDRESSES) {
    empty_slot_r = 1;
    path_table_ret[0] = addr;
  } else {
    path_table_ret[empty_slot_r++] = addr; 
  }
}

void PredictorRunACycle() {
    // get info about what uops are processed at each pipeline stage
    const cbp3_cycle_activity_t *cycle_info = get_cycle_info();

    // make prediction at fetch stage
    for (int i = 0; i < cycle_info->num_fetch; i++) {
        uint32_t fe_ptr = cycle_info->fetch_q[i];
        const cbp3_uop_dynamic_t *uop = &fetch_entry(fe_ptr)->uop;

        if (/* runs == 0 && */uop->type & IS_BR_CONDITIONAL) {
	  uint32_t index = computeHash_fetch(MAX_ADDRESSES); //Get index into predictor
	  bool pred = (prediction_table[(index ^ uop->pc) & ((1 << PRED_SIZE) - 1)] >= 0);


	  assert(report_pred(fe_ptr, false, pred));
        }else if (/* runs == 1 && */ uop->type & IS_BR_INDIRECT) {
       
	}
	
	add_addr_fetch(uop->br_target);

    }

    for (int i = 0; i < cycle_info->num_retire; i++) {
        uint32_t rob_ptr = cycle_info->retire_q[i];
        const cbp3_uop_dynamic_t *uop = &rob_entry(rob_ptr)->uop;

        if (/*runs == 0 &&*/ uop->type & IS_BR_CONDITIONAL) {

	  uint32_t index = computeHash_ret(MAX_ADDRESSES); //Get index into predictor
	  bool pred = (prediction_table[(index ^ uop->pc) & ((1 << PRED_SIZE) - 1)] >= 0);

          bool t = uop->br_taken;
          if (t && prediction_table[index] < 1)
                prediction_table[index]++;
          else if (!t && prediction_table[index] > -2)
                prediction_table[index]--;
        }else if (/*runs == 1 &&*/ uop->type & IS_BR_INDIRECT) {
           }
	
	add_addr_ret(uop->br_target);
    }
}


void PredictorRunEnd() {
/*
    runs ++;
    if (runs < 2) // set rewind_marked to indicate that we want more runs
        rewind_marked = true;
*/
}

void PredictorExit() {
  }
